#include "StdAfx.h"
#include "ConfigParser.h"


ConfigParser::ConfigParser(const std::string& fileName)
	:currentRoot(NULL), defaultSection(NULL)
{
	std::wstring wFileName;
	string2wstring(fileName, wFileName);
	currentFile.open(wFileName, std::ios_base::in);

	if(!currentFile.is_open())
	{
		cerror("Open file " << fileName << " Failed.");
		return;
	}

	// start parsing
	currentRoot = new TokenNode(TOKEN_ROOT, NULL);
	defaultSection = new TokenNode(TOKEN_SECTION, "default");
	currentRoot->addChild(defaultSection);
	currentNode = NULL;
	parse();
	currentFile.close();
}


ConfigParser::~ConfigParser(void)
{
	// delete tree
	// no need to delete defaultSection and currentNode because they are under root
	delete currentRoot;
}

const char* ConfigParser::getFirstValueByName(const std::string& name, const char* sectionName) const
{
	const TokenNode* rtNode = search(name, sectionName);
	if (rtNode)
	{
		return rtNode->getData();
	}

	return NULL;
}

void ConfigParser::getAllValuesByName(const std::string& name, const char* sectionName, char** outValues) const
{
	// todo
}

void ConfigParser::parse()
{
	currentFile.get(nextChar);
	currentNode = readNextToken();
	if (!currentNode) return;

	while(currentNode->getToken() != TOKEN_EOF)
	{
		if (currentNode->getToken() == TOKEN_SECTION)
		{
			currentRoot->addChild(currentNode);
			TokenNode* currentSection = currentNode;
			currentNode = readNextToken();
			parseSection(currentSection);
		}
		else
		{
			parseSection(defaultSection);
		}
	}

	// eof node is not on the tree, so we have to delete it separately
	if (currentNode->getToken() == TOKEN_EOF)
	{
		delete currentNode;
	}
}

TokenNode* ConfigParser::readNextToken()
{
	if (currentFile.eof())
	{
		return new TokenNode(TOKEN_EOF, NULL);
	}

	TokenNode* result = NULL;

	switch (nextChar)
	{
	case '=':
		result = new TokenNode(TOKEN_EQUAL, NULL);
		currentFile.get(nextChar);
		break;

	case '[':
		{
			currentFile.get(nextChar);

			char* variable = new char[MAX_CONFIG_NAME_LENGTH];
			int iLength = 0;
			// cut to MAX_CONFIG_NAME_LENGTH if exceed
			while (nextChar != '\n' && nextChar != ']' && iLength <= MAX_CONFIG_NAME_LENGTH - 1)
			{
				variable[iLength++] = nextChar;
				currentFile.get(nextChar);
			}
			variable[iLength] = '\0';
			result = new TokenNode(TOKEN_SECTION, variable);
			delete[] variable;
			// skip ']'
			currentFile.get(nextChar);
		}
		break;

	case '\\':
		currentFile.get(nextChar);
		if (nextChar == '\\')
		{
			currentFile.get(nextChar);

			// cut to MAX_CONFIG_NAME_LENGTH if exceed
			while (nextChar != '\n')
			{
				currentFile.get(nextChar);
			}
		}
		return readNextToken();

	case '\n':
	case ' ':
		currentFile.get(nextChar);
		return readNextToken();

	default:
		{
			char* variable = new char[MAX_CONFIG_NAME_LENGTH];
			int iLength = 0;
			// cut to MAX_CONFIG_NAME_LENGTH if exceed
			while (isLegalExpression(nextChar) && iLength <= MAX_CONFIG_NAME_LENGTH - 1)
			{
				variable[iLength++] = nextChar;
				currentFile.get(nextChar);
			}
			variable[iLength] = '\0';
			result = new TokenNode(TOKEN_VARIABLE, variable);
			delete[] variable;
		}
		break;
	}

	return result;
}

void ConfigParser::parseSection(TokenNode* section)
{
	while(currentNode->getToken() != TOKEN_EOF && currentNode->getToken() != TOKEN_SECTION)
	{
		TokenNode* lChild = currentNode;
		TokenNode* eq = readNextToken();
		TokenNode* rChild = readNextToken();

		section->addChild(eq);
		parseExpression(eq, lChild, rChild);

		currentNode = readNextToken();
	}
}

void ConfigParser::parseExpression(TokenNode* eq, TokenNode* lChild, TokenNode* rChild)
{
	if (eq->getToken() == TOKEN_EQUAL)
	{
		eq->addChild(lChild);
		eq->addChild(rChild);
	}
	// todo else report error
}

bool ConfigParser::isLegalExpression(const char& ch) const
{
	return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-');
}

const TokenNode* ConfigParser::search(const std::string& name, const char* sectionName) const
{
	if (sectionName == NULL)
	{
		sectionName = "default";
	}

	const TokenNode* sectionNode = NULL;
	for (size_t i = 0; i < currentRoot->getNumChildren(); ++i)
	{
		sectionNode = currentRoot->getChild(i);
		if (strcmp(sectionNode->getData(), sectionName) == 0)
		{
			break;
		}
	}

	if (sectionNode)
	{
		for (size_t i = 0; i < sectionNode->getNumChildren(); ++i)
		{
			const TokenNode* node = sectionNode->getChild(i);
			// equal should always have and only have two children
			if (node && node->getToken() == TOKEN_EQUAL)
			{
				const TokenNode* lChild = node->getChild(0);
				if (lChild && strcmp(lChild->getData(), name.c_str()) == 0)
				{
					// return right child
					return node->getChild(1);
				}
			}
		}
	}

	return NULL;
}